<template>
  <div id="fullScreenElem">
    <el-row :gutter="24">

      <el-col :span="24">
        <el-card style="margin-bottom: 10px">

          距离考试结束还有：
          <TimeCountDown :lastTime='this.timeInfo.lastTime' :firstEnter='this.timeInfo.firstEnter' :endTime='this.timeInfo.endTime' @timeout="doHandler()"></TimeCountDown>

          <el-button v-if="!showAll" style="float: right; margin-top: -10px" type="primary" :loading="loading" @click="handHandExam()">
            交卷
          </el-button>

          <div v-else style="float: right; margin-top: -10px">
            <el-button type="success" icon="el-icon-check" size="mini" @click="handInPaper">确认交卷</el-button>
            <el-button icon="el-icon-circle-close" size="mini" @click="changeShowAll">返回</el-button>
          </div>
        </el-card>
      </el-col>
 
      <div v-if="!showAll">

        <!-- 答题卡 -->
        <el-col :span="6" :xs="24" style="margin-bottom: 10px">

          <el-card>

            <p class="card-title">答题卡</p>
            <el-row :gutter="24" class="card-line" style="padding-left: 10px">
              <el-tag type="info">未作答</el-tag>
              <el-tag type="success">已作答</el-tag>
            </el-row>

            <div v-for = "(questions,qType ,i) in paperData" :key="i">
              <p class="card-title">{{questionType(qType)}}</p>
              <el-row :gutter="24" class="card-line">
                <el-tag  v-for="(q,j) in questions" :key="j" :type="cardItemClass(q.answer,q.id)" @click="handSave(q)"> {{ q.tiNumber }}</el-tag>
              </el-row>
            </div>

            <!-- 摄像头占位 -->
            <faceShot style="visibility:hidden" @refreshImgData="faceIsOk"></faceShot> 

          </el-card>

        </el-col>

        <!-- 正中央一道题显示界面 -->
        <el-col :span="18" :xs="24">

          <el-card class="qu-content">
            <p v-if="cardItem">{{ cardItem.tiNumber}}.<span v-html="cardItem.name"></span></p>
            <!-- 答案区域 -->
            <div v-if="cardItem.type==='1'||cardItem.type==='2'">
              <el-row :gutter="10">
                <el-col :span="8"><h4>选择题答案只能输入大写字母</h4></el-col>
              </el-row>
              <el-row :gutter="10">
                <el-col :span="6">
                  <el-input v-if="cardItem.type==='1'"
                      placeholder="请输入内容"
                      v-model="cardItem.answer" 
                      onkeypress="return(/[A-Z]/.test(String.fromCharCode(event.keyCode)))" 
                      maxlength="1"
                      show-word-limit>
                  </el-input>
                  <el-input v-else-if="cardItem.type==='2'" 
                      placeholder="请输入内容" 
                      v-model="cardItem.answer" 
                      onkeypress="return(/[A-Z]/.test(String.fromCharCode(event.keyCode)))"
                      maxlength="8"
                      show-word-limit>
                  </el-input>
                </el-col>
              </el-row>
            </div>
            <div v-else-if="cardItem.type==='3'"> 
              <el-radio v-model="cardItem.answer" label="对">对</el-radio>
              <el-radio v-model="cardItem.answer" label="错">错</el-radio>
            </div>
            <div id="questionContent" v-else-if="!(cardItem.type==='1'||cardItem.type==='2'||cardItem.type==='3')">
              <vue-tinymce v-model="cardItem.answer" :setting="setting" />
            </div>
          </el-card>
          
          <!-- 切换题目按钮 -->
          <div style="margin-top: 20px">
            <el-button v-if="showPrevious" type="primary" icon="el-icon-back" @click="handPrevious()">
              上一题
            </el-button>

            <el-button v-if="showNext" type="warning" icon="el-icon-right" @click="handNext()">
              下一题
            </el-button>

          </div>

        </el-col>

      </div>

      <!-- 总览 -->
      <div v-else style="padding: 10px 5%" class="box-card">
        <!-- 试卷展示 -->
        <div v-for = "(questions,qType ,i) in paperData" :key="i">
            <hr>
            <h4>{{questionType(qType)}}</h4> 
            <el-card  v-for="(q,j) in questions" :key="j" style="padding: 10px 20px">
                <el-divider><span style="color: rgb(239,160,48)">(分值：{{q.score}})</span></el-divider>
                <p class="title">第{{j+1}}题</p>
                <span class="title" v-html="q.name" style="margin: 20px"></span>
                <br>
                <br>
                <p>答案：</p>
                <span v-html="q.answer" style="margin: 20px"></span>
            </el-card >
        </div>
        <!-- 按钮 -->
        <div style="text-align:center">
          <el-button type="success" icon="el-icon-check" size="mini" @click="handInPaper">确认交卷</el-button>
          <el-button icon="el-icon-circle-close" size="mini" @click="changeShowAll">返回</el-button>
        </div>
      </div>

    </el-row>
  </div>
</template>

<script>
import { getStudentId } from '@/api/user'
import faceShot from './faceShot'
import { checkPhotoForExam } from "@/api/user";
export default {
  name:'startExam',
  components:{
    faceShot,
  },
  data() {
    return {
      // 倒计时需要的东西
      timeInfo:{
        // 持续时间
        lastTime:0,
        // 第一次进入考试时间
        firstEnter:'',
        // 结束时间
        endTime:'',
      },
      // 试卷接口切换时间
      secondsChange:120,
      // socket
      socket:null,
      // socket消息
      msg:{},
      // 全展示
      showAll:false,
      exam_info:{
        id:'',
        //试卷id
        paperId:'',
        //考试名称
        name:'',
        //备注
        remark:'',
        //是否在考试中开启人脸认证
        isFaceCheck:'',
        //限时进入（分钟）
        timeLimit:'',
        //是否可以提前交卷
        isAdvance:'',
        //是否支持复制黏贴
        isCopyPaste:'',
        //开始时间
        startTime:'',
        //结束时间
        endTime:'',
        //持续时间（分钟）
        lastTime:'',
        //创建时间
        gmtCreate:'',
        //修改时间
        gmtModified:'',
      },
      // 提交试卷按钮转圈圈
      loading: false,
      //上一道题，下一道题按钮显示问题
      showPrevious: false,
      showNext: true,
      // 当前答题卡
      cardItem: {},
      // 题目总数组
      allItem:[],
      // 试卷信息
      paperData: {},
      // 存储一下题型字典
      typeList:[],
      // 编辑器配置问题
      setting: {
        //翻译语言
        language:'zh_CN',
        //字体
        font_formats: '微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;Times New Roman;Verdana, Arial, Helvetica, sans-serif;仿宋=fangsong',
        //字号
        fontsize_formats: '11px 12px 14px 16px 18px 24px 36px 48px',
        //设置输入框的参数
        //指定编辑器body初始化时底边距
        autoresize_bottom_margin: 50,
        //限定编辑器的最大高度
        max_height: 350,
        //限定编辑器的最小高度。
        min_height: 200,
        //指定编辑器body初始化时两侧的距离
        autoresize_overflow_padding: 25,
        //上传图片的接口
        images_upload_url: '/dev-api/examoss/fileoss/upFile',
        //想要修改图片，得实现的参数

        //限制图片大小
        content_style: "img {max-width:100%;}",
        //快速插入工具
        quickbars_insert_toolbar: 'quickimage',
        //隐藏底边栏
        //statusbar: false,
        //对images_upload_url中指定的地址调用时是否传递cookie等跨域的凭据
        images_upload_credentials: true,
        //撤销次数
        custom_undo_redo_levels: 10,
        // 菜单栏 
        menubar: 'bar1 bar2 bar3 bar4 bar5',
        menu: {
          bar1: {title:'文件',items:'newdocument'},
          bar2: {title:'编辑',items:'undo redo | cut copy paste | selectall'},
          bar3: {title:'查看',items:'visualaid code'},
          bar4: {title:'插入',items:'hr charmap codesample emoticons | image'},
          bar5: {title:'格式',items:'bold italic underline strikethrough superscript subscript | removeformat'},
        },
        // 工具栏
        toolbar: [
          'undo redo restoredraft | styleselect fontselect fontsizeselect | bold italic bullist numlist | preview fullscreen | charmap codesample emoticons image',
          'forecolor backcolor | searchreplace table wordcount'
        ],
        // 插件 preview预览
        plugins: 'wordcount table image preview lists advlist autoresize code charmap codesample emoticons fullscreen hr table link searchreplace',
        height: 350
      },
      // 提前交卷的flag
      advanceFlag: false,
    }
  },
  mounted() {
    this.exam_info = JSON.parse(this.$route.query.exam_info);
    //建立连接，发送消息
    this.openSocket();
    this.getTypeList();
    this.getStudentConnection();
    this.getPaper();
  },
  beforeDestroy(){
    this.global.ws.close();
  },
  methods: {
    // 获得学生各种状态，用于定时器目前
    async getStudentConnection(){
      let result = await this.$API.coming_exam.getExamStudentConnection(this.exam_info.id);
      if(result.code==20000){
        this.timeInfo.lastTime = this.exam_info.lastTime;
        this.timeInfo.firstEnter = result.data.connection.firstEnter;
        this.timeInfo.endTime = this.exam_info.endTime;
      }
    },
    // 获得试卷用户函数
    getPaper(){
      const endTime = new Date(this.exam_info.startTime)
      const nowTime = new Date();
      let leftTime = parseInt((nowTime.getTime()-endTime.getTime())/1000)
      if(leftTime>this.secondsChange){
        this.getPaperData();
      }else{
        this.getPaperDataFirst();
      }
    },
    // 第一次获得试卷
    async getPaperDataFirst(){
      let result = await this.$API.coming_exam.takeOneExamFirst(this.exam_info.paperId);
      if(result.code==20000){
        this.paperData = result.data.question;
        // 获得第一题内容
        for(var i=0;i<this.typeList.length;i++){
          // 获取键key
          var key = this.typeList[i].id;
          // 寻找第一个有题目的题目类型
          if (this.paperData[key]) {
            this.cardItem = this.paperData[key][0];
            break;
          }
        }

        // 想要保存所有题目
        const that = this
        // 这么做的无奈之处是，马子说题型万一动起来了咋办，键key我假装不知道，循环处理
        var indexNum = 1;
        for(var i=0;i<this.typeList.length;i++){
          // 获取键key
          var key = this.typeList[i].id;
          if(this.paperData[key]){
            this.paperData[key].forEach(function(item) {
              that.allItem.push(Object.assign(item,{tiNumber:indexNum}));
              indexNum++;
            })
          }
        }

        // 逻辑上这时去渲染题目
        this.fetchData(this.cardItem);
      }
    },
    // 人脸验证
    async faceIsOk(image){
      let result = await checkPhotoForExam(image, this.exam_info.id);
      // if (result.code == 20000) {
      //   //成功的画不会弹出消息
      // }
      if(result.code == 1){
        alert("未识别到人脸");
      }
      else if(result.code == 2){
        alert("检验到多张人脸，无关人员请离开");
      }
      else if(result.code == 3){
        alert("识别错误！");//可以采用三次识别错误就终止考试的方式
      }
    },
    // 发送消息
    async sendMessage() {
      if(typeof(WebSocket) == "undefined") {
          console.log("您的浏览器不支持WebSocket");
      }else {
          // console.log("您的浏览器支持WebSocket");
          var toUserId = '';
          let result = await getStudentId();
          if(result.code==20000){
            toUserId = result.data.studentId;
          }
          toUserId += '-'+this.exam_info.id + '-0';
          var msgInfo = document.getElementById('msgInfo').value;
          var msgType = document.getElementById('msgType').value;
          var msg = '{"toUserId":"'+toUserId+'","msgInfo":"'+msgInfo+'","msgType":"'+msgType+'"}';
          console.log(msg);
          socket.send(msg);
      }
    },
    // 打开连接
    async openSocket() {
      if(typeof(WebSocket) == "undefined") {
          console.log("您的浏览器不支持WebSocket");
      }else{
          console.log("您的浏览器支持WebSocket");
          //实现化WebSocket对象，指定要连接的服务器地址与端口  建立连接
          var that = this;
          var singleId = '';
          let result = await getStudentId();
          if(result.code==20000){
            singleId = result.data.studentId;
          }
          singleId += '-'+this.exam_info.id + '-0';
          // var socketUrl="ws://127.0.0.1:22599/webSocket/"+userId;
          var socketUrl="ws://jnexam.natapp1.cc/webSocket/web-socket-server/"+singleId;
          console.log(socketUrl);
          if(this.socket != null){
              this.socket.close();
              this.socket=null;
          }
          this.socket = new WebSocket(socketUrl);
          //替代全局里的ws，方便调用
          this.global.setWs(this.socket);
          console.log(this.global.ws);
          //打开事件
          this.socket.onopen = function() {
              console.log("websocket已打开");
              //socket.send("这是来自客户端的消息" + location.href + new Date());
          };
          //获得消息事件
          this.socket.onmessage = function(msg) {
              var serverMsg = "收到服务端信息：" + msg.data;
              console.log(serverMsg);
              //发现消息进入    开始处理前端触发逻辑
              that.msg = JSON.parse(msg.data);
              if(that.msg.msgType==2){
                  that.$message.error('你已被认定为作弊，即将退出考试！！！');
                  setTimeout(() => {that.$router.replace({ name: 'Coming_exam' })}, 2000);
              }
          };
          //关闭事件
          this.socket.onclose = function() {
              console.log("websocket已关闭");
          };
          //发生了错误事件
          this.socket.onerror = function() {
              console.log("websocket发生了错误");
          }
      }
    },
    // 提前交卷的判断
    canAdvanceHandIn(){
      // 提前交卷时间,总时间
      var {timeAdvance,lastTime} = this.exam_info; 
      // 该生开始考试时间
      var {firstEnter} = this.timeInfo;
      // 要前后都满足条件才能提前交卷，
      const endTime = new Date(firstEnter)
      const nowTime = new Date();
      // 当前时间-开始时间,判断刚开考的情况
      let leftTime = parseInt((nowTime.getTime()-endTime.getTime())/1000)
      if(leftTime<timeAdvance*60){
        // 太早，不能提前交卷
        this.advanceFlag = false;
        this.$message({
          message: '考试时间过短，无法提前交卷',
          type: 'warning'
        });
      }else if(leftTime>(lastTime-timeAdvance)*60){
        // 当前时间-开始考试时间> 总时间-提前交卷时间
        // 后半截不符合条件
        this.advanceFlag = false;
        this.$message({
          message: '考试快结束，无法提前交卷',
          type: 'warning'
        });
      }else{
        this.advanceFlag = true;
      }
    },
    // 真正的交卷 
    async handInPaper(){
      this.canAdvanceHandIn();
      if(this.advanceFlag){
        const that = this;
        // 交卷需要等待
        const loading = this.$loading({
          lock: true,
          text: '试卷打包中，请不要刷新界面，等待自动跳转...',
          spinner: 'el-icon-loading',
          background: 'rgba(0, 0, 0, 0.7)'
        });
        // 此处应该要关闭socket
        this.global.ws.close();

        // 调用交卷接口函数,据说怕交卷会卡，建议打包试卷的状态久一点
        let studentConnection = {
          examId:this.exam_info.id,
          studentId:this.studentId,
          // 考试得分
          progress:0,
          // 是否作弊
          isCheated:false,
          // 是否以阅卷
          isMarked:-1,
          // 是否参加考试
          isAttended:1,
        }
        setTimeout(() => {}, 2000);
        let result = await this.$API.coming_exam.updateExamStudentConnectionFromStu(studentConnection);
        // 成功交卷后，跳转到考试界面吧
        if(result.code==20000){
          setTimeout(() => {
            loading.close();
            that.$router.push({ 
              name: 'Coming_exam',
            })
          }, 2000);
        }
      }
    },
    // 改变全览的状态
    changeShowAll(){
      this.showAll = !this.showAll;
    },
    // 把当前的cardItem上传保存
    async submitItem(){
      // 完成数量
      var completeNum = this.allItem.length - this.countNotAnswered();
      var progressFloat = completeNum / this.allItem.length;
      let anwerBody = {
        paperQuestionId:this.cardItem.id,
        answer:this.cardItem.answer,
        progress: progressFloat,
      }
      let result = await this.$API.coming_exam.sendPaperQuestionAnswer(this.exam_info.id,anwerBody);
      if(result.code==20000){
        this.allItem[this.cardItem.tiNumber-1].answer = this.cardItem.answer;
        this.$message({
          message: '保存成功',
          type: 'success'
        });
      }
    },
    // 拿一道题目
    fetchData(item){
      // 获得详情
      this.cardItem = item;
      // 有个小bug，当要编辑器的时候，他接受不了答案是null,估计得是个空字符串
      if(!this.cardItem.answer){
        this.cardItem.answer='';
      }
    },
    // 通过题目选项卡切换，趁机保存答案
    handSave(item) {
      // 第一题，不显示pre按钮
      if (item.id === this.allItem[0].id) {
        this.showPrevious = false
      } else {
        this.showPrevious = true
      }

      // 最后一个索引
      const last = this.allItem.length - 1
      // 最后一道题，应该不能有next按钮
      if (item.id === this.allItem[last].id) {
        this.showNext = false
      } else {
        this.showNext = true
      }

      // 此处应该先保存当前cardItem里的题目答案，
      // 不过可以判断一下有没有答案，没有不保存，有要调用一次接口
      if(this.cardItem.answer){
        this.submitItem();
      }
      
      // 切换当前显示题目的内容
      this.fetchData(item);
      

    },
    // 答题卡样式
    cardItemClass(answer,currentId) {
      //这玩意是一次性遍历所有题目，检测的
      //这个状态是当前选中的题目编号，会变成warning色
      if (currentId === this.cardItem.id) {
        return 'warning'
      }

      if (answer) {
        return 'success'
      }

      if (!answer) {
        return 'info'
      }
    },
    // 返回题目的类型
    questionType(q){
        return this.typeList[q*1-1].type;
    },
    // 获得题型字典
    async getTypeList() {
      //获取专业分类的请求，不需要携带参数
      let result = await this.$API.coming_exam.reqGetTypeList();
      if(result.code==20000){
        this.typeList = result.data.typeList;
      }
    },
    // 获得变化的试卷信息,学生刷新也不怕
    async getPaperData(){
      let result = await this.$API.coming_exam.takeOneExam(this.exam_info.paperId);
      if(result.code==20000){
        this.paperData = result.data.question;
        // 获得第一题内容
        for(var i=0;i<this.typeList.length;i++){
          // 获取键key
          var key = this.typeList[i].id;
          // 寻找第一个有题目的题目类型
          if (this.paperData[key]) {
            this.cardItem = this.paperData[key][0];
            break;
          }
        }

        // 想要保存所有题目
        const that = this
        // 这么做的无奈之处是，马子说题型万一动起来了咋办，键key我假装不知道，循环处理
        var indexNum = 1;
        for(var i=0;i<this.typeList.length;i++){
          // 获取键key
          var key = this.typeList[i].id;
          if(this.paperData[key]){
            this.paperData[key].forEach(function(item) {
              that.allItem.push(Object.assign(item,{tiNumber:indexNum}));
              indexNum++;
            })
          }
        }

        // 逻辑上这时去渲染题目
        this.fetchData(this.cardItem);
      }
    },
    // 下一道题响应函数
    handNext(){
      const index = this.cardItem.tiNumber
      this.handSave(this.allItem[index])
    },
    // 上一道题响应函数
    handPrevious(){
      const index = this.cardItem.tiNumber - 2
      this.handSave(this.allItem[index])
    },
    // 统计没有答案的题目数量
    countNotAnswered() {
      let notAnswered = 0

      this.allItem.forEach(function(item) {
        if (!item.answer) {
          notAnswered += 1
        }
      })

      return notAnswered
    },
    // 进入预览界面
    enterAllDetails(){
      // 考虑答案不一定能实时再次通过
      // 考虑页面直接整个上移，用一个card用已有的信息展示试卷
      this.showAll = true;
    },
    // 初步交卷，第一道门槛
    // 如果题目未全部答完，发送弹窗确定交卷吗
    // 进入试卷全局展览，方便检查
    // 最后一道题，可能还没提交，这里再次保存一下
    handHandExam(){
      // 把当前题目在保存一次，保证全部提交
      this.submitItem();
      // 判断题目答题情况
      let noAnswerNum = this.countNotAnswered();
      if(noAnswerNum!=0){
        //还有题目没答完，提出弹窗，提醒考生
        //弹框，但不致命，因为此时
        this.$confirm(`你还有${noAnswerNum}道题目还未提交答案，你确定交卷么?`, '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
        }).then(async() => {
          //当用户点击确定按钮时会触发
          //学生想要强行进入预览界面，最后一面
          this.enterAllDetails();
          }).catch(() => {
          //当用户点击取消按钮时会触发
            this.$message({
              type: 'info',
              message: '注意是否答案已填涂'
            });          
        });
      }else{
        // 题目都答完，那不是顺理成章，直接进入预览模式
        this.enterAllDetails();
      }
    },
    // 考试时间结束的处理
    doHandler(){
      // 直接调用交卷接口
      this.handInPaper();
    }
  },
}
</script>

<style>
.title{
  font-size: 19px;
  font: bold;
}
.qu-content div{
    line-height: 30px;
}
.card-title{
    background: #eee;
    line-height: 35px;
    text-align: center;
    font-size: 14px;
}
.card-line{
  padding-left: 10px
}
.card-line span {
  cursor: pointer;
  margin: 2px;
}
</style>